package org.franca.importer.ea.post;

import java.util.HashMap;
import java.util.concurrent.BrokenBarrierException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.franca.core.dsl.FrancaIDLHelpers;
import org.franca.core.franca.FEnumerationType;
import org.franca.core.franca.FField;
import org.franca.core.franca.FModel;
import org.franca.core.franca.FStructType;
import org.franca.core.franca.FUnionType;
import org.franca.importer.ea.utils.EAConstants;
import org.franca.importer.ea.utils.EAConstants.ECardinality;
import org.franca.importer.ea.utils.EAConstants.EConnectionType;
import org.franca.importer.ea.utils.EAConstants.EConnectorDirection;
import org.franca.importer.ea.utils.EAConstants.EStereoType;
import org.franca.importer.ea.utils.EARepositoryAccessor;
import org.junit.Assert;
import org.sparx.Attribute;
import org.sparx.Connector;
import org.sparx.ConnectorEnd;
import org.sparx.Element;
import org.sparx.Package;

public class ConnectorChecker  extends PostProcessorDecorator {

	private static Logger jlog =  Logger.getLogger(ConnectorChecker.class.getName());
	
	public ConnectorChecker(PostProcessor lProcessor) {
		super(lProcessor);
	}

	@Override
	public void doConsistencyCheckForPackage(Package eaPackage, FModel fModel) {
		
		processor.doConsistencyCheckForPackage(eaPackage, fModel);
				
		updateConnections(eaPackage);				
	}

	private void updateConnections(Package _package) {
		
		jlog.log(Level.FINE, "Updating all connectors for Package "+_package.GetName());
				
		for(Package p: _package.GetPackages()) {						
			updateConnections(p);
		}

		// Delete all connectors if available
		for(Element e: _package.GetElements()) {			
			for(short i=0; i<e.GetConnectors().GetCount(); i++) {
				jlog.log(Level.FINE, "Deleting Connector " +e.GetConnectors().GetAt(i).GetName());
				e.GetConnectors().Delete(i);
			}
			e.Refresh();
		}
		// 
		
		
		for(Element e: _package.GetElements()) {
								
			if(e.GetStereotype().equals(EStereoType.INTERFACE.getName())) {
				Element bI = EARepositoryAccessor.INSTANCE.findElement(e.GetName()+EAConstants.BROADCASTINTERFACE_SUFFIX);
				if(bI != null) {					
					 createConnector("FrancaInterface", e, bI,  
								EAConstants.EConnectorDirection.UNSPECIFIED, 
								EAConstants.EConnectionType.BROADCAST, 
								null);						
				}
				
				e.GetConnectors().Refresh();					
			}
			
			else if(e.GetStereotype().equals(EStereoType.STRUCTURE.getName()) || 
			   e.GetStereotype().equals(EStereoType.UNION.getName()) || 
			   e.GetStereotype().endsWith(EStereoType.MAP.getName())) {
								
				short numConn = 0;
				
				// Create Connectors
				for(Attribute a: e.GetAttributes()) {
					jlog.log(Level.FINE, "Search type for attribute : "+a.GetName());
					Element type = EARepositoryAccessor.INSTANCE.findElement(a.GetType());
					if(type != null) {
						Connector conn = null;
							
						EConnectionType connType = null;
						
						if(e.GetStereotype().equals(EStereoType.STRUCTURE.getName())) {
							connType = EConnectionType.STRUCTURE;
						}
						else if(e.GetStereotype().equals(EStereoType.UNION.getName())) {
							connType = EConnectionType.UNION;
						}
						else if(e.GetStereotype().equals(EStereoType.MAP.getName())) {
							connType = EConnectionType.MAP;
						}
						
						if(a.GetIsCollection()) {						
							conn = createConnector(a.GetName(), e, type,  
									EConnectorDirection.SOURCE_TO_DEST, 
									connType,
									ECardinality.ZERO_TO_INFINITE);
						}
						else {
							conn = createConnector(a.GetName(), e, type,  
									EAConstants.EConnectorDirection.SOURCE_TO_DEST, 
									connType, 
									null);							
						}
						
						numConn++;
					}
				}
				
				e.GetConnectors().Refresh();
				
				if(numConn != e.GetConnectors().GetCount()) {
					jlog.log(Level.SEVERE, e.GetName()+ " Expected connectors "+numConn+" but found "+e.GetConnectors().GetCount());
					for(Connector c: e.GetConnectors()) {
						jlog.log(Level.INFO, c.GetName());
					}
				}
				
			}
			else if(e.GetStereotype().equals(EStereoType.ARRAY.getName()) || 
					e.GetStereotype().equals(EStereoType.ALIAS.getName())) {
								
				//Attribute a = e.GetAttributes().GetAt((short) 0);
				//Element type = EARepositoryAccessor.INSTANCE.findElement(a.GetType());
				String elementType = e.GetGenlinks().split("=")[1].replaceFirst(";", "");
				Element type = EARepositoryAccessor.INSTANCE.findElement(elementType);
				
				if(type != null) {
					 createConnector("element type", e, type,  
								EAConstants.EConnectorDirection.DEST_TO_SOURCE, 
								EAConstants.EConnectionType.ARRAY, 
								null);						
				}
				
				e.GetConnectors().Refresh();
				
			}
			
		}
		
	}
	
	
	@Override
	public void doConsistencyCheckForStructure(Element eaStruct, FStructType fStruct) {						
		processor.doConsistencyCheckForStructure(eaStruct, fStruct);					
	}

	private Connector createConnector(String name, Element supplier, Element client, 
			EConnectorDirection direction,
			EConnectionType type,
			ECardinality cardinality) {

		Connector conn = supplier.GetConnectors().AddNew("", "");																		
		
		//Element supplier = mConnectors.get(getTypeName(src.getType()));
		conn.SetClientID(client.GetElementID());
		conn.SetSupplierID(supplier.GetElementID());
		conn.SetDirection(direction.getName());
		conn.SetType(type.getName());
		
		if(type.getName().equals("Composition")) {
			conn.SetSubtype("Strong");
		}
		
		conn.SetName(name);
		
		//check cardinality
		if(cardinality != null) {
			ConnectorEnd end = conn.GetClientEnd();
			end.SetCardinality(cardinality.getName());			
		}
		
		conn.Update();
		
		StringBuffer buf = new StringBuffer("Id: ");
		buf.append(conn.GetConnectorID()).append(", Name: ").append(conn.GetName()).
		append(", Type: ").append(conn.GetType()).
		append(", Direction: ").append(conn.GetDirection()).append(", ClientId: ").
		append(conn.GetClientID()).append(", SupplierId: ").append(conn.GetSupplierID());
		
		jlog.log(Level.FINE, "Created connector: "+buf.toString());
		
		return conn;
	}
				
	@Override
	public void doConsistencyCheckForEnumeration(Element eaEnum, FEnumerationType fEnum) {

		processor.doConsistencyCheckForEnumeration(eaEnum, fEnum);
				
	}

	@Override
	public void doConsistencyCheckForUnion(Element eaStruct, FUnionType fUnion) {

		processor.doConsistencyCheckForUnion(eaStruct, fUnion);
	}

}
